home *** CD-ROM | disk | FTP | other *** search
/ Power DOS 1996 July / Power DOS - July 1996.iso / sound / c_labs / devinfo / autoinit.exe / DMAV / DMAV.C < prev    next >
Text File  |  1996-02-09  |  65KB  |  1,981 lines

  1. /****************************************************************************
  2. *                                                                           *
  3. *  (C) Copyright Creative Technology Ltd. 1994-1996. All rights reserved.   *
  4. *                                                                           *
  5. *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY    *
  6. *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE      *
  7. *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR    *
  8. *  PURPOSE.                                                                 *
  9. *                                                                           *
  10. * You have a royalty-free right to use, modify, reproduce and               *
  11. * distribute the Sample Files (and/or any modified version) in              *
  12. * any way you find useful, provided that you agree that                     *
  13. * Creative has no warranty obligations or liability for any Sample Files.   *
  14. *                                                                           *
  15. ****************************************************************************/
  16. /*************************************************************************
  17. *
  18. * TITLE: DMAV.C
  19. *
  20. * AUTHOR: Tom Bouril  (October 1994)
  21. *
  22. * DANGER: Neither author of this program nor Creative Labs, Inc. is
  23. *         responsible for any problem(s) that may occur as a result of
  24. *         using this code.
  25. *
  26. * COMPILER: Turbo C/C++ Version 1.01.  (Works with Borland C/C++).  This
  27. *           program uses only C code.  No C++ instructions are used.  The
  28. *           only C++ code used is the double slash (//) comments.
  29. *
  30. * DESCRIPTION:  This program will play 8-bit and 16-bit .VOC files of type
  31. *               PCM and CTADPCM.  Only block types 0, 1, 8, and 9 are
  32. *               supported.  This program will detect the card's DSP version
  33. *               and program the card according to its type.  All that is
  34. *               required is a BLASTER environment variable containing the
  35. *               following information.  Make sure the jumper settings of
  36. *               the card reflect the numbers in the BLASTER environment.
  37. *
  38. *               SET BLASTER=A2w0 Ix Dy Hz
  39. *
  40. *               Where:  A represents the base I/O address of the SB.
  41. *                       I represents the interrupt request number of the SB.
  42. *                       D represents the 8-bit DMA channel number used by SB.
  43. *                       H represents the 16-bit DMA channel number used by SB.
  44. *                       w = 2, 4, 6, or 8
  45. *                       x = 2, 3, 5, 7, 10
  46. *                       y = 0, 1, or 3
  47. *                       z = 5, 6, or 7
  48. *
  49. * NOTE: If you want to play 16-bit CTADPCM files you need a Sound Blaster
  50. *       SB16 or AWE32 with the CSP chip.  In order to use the CSP chip,
  51. *       you must do: #define USE_CSP.  Without that #define statement,
  52. *       the CSP relevant code is not compiled.  The CSP.SYS driver must
  53. *       also be loaded via CONFIG.SYS upon boot up.
  54. *
  55. * INSTRUCTIONS:  Enter the following on the command line.
  56. *
  57. *                >DMAV filename.VOC 2000
  58. *
  59. *                The 2000 is the number of bytes of the DMA buffer.  You
  60. *                may give it any value, but it's value is not a multiple
  61. *                of 8, it will be bumped up to the nearest multiple of 8.
  62. *
  63. *                The file being played can be paused and resumed by
  64. *                hitting the spacebar key.  The program can be exited
  65. *                by pressing the Esc or 'Q' key.
  66. *
  67. *
  68. * RECOMMENDED DMA BUFFER SIZES
  69. * ----------------------------
  70. *
  71. * The following is a chart of recommended DMA buffer sizes required
  72. * for the types of files you may need to play.  On faster systems (i.e., 486)
  73. * very small DMA buffer sizes (less than 50 bytes) may work well for low
  74. * sample rate files (i.e., 11025 samples per sec., mono); however, for
  75. * safe tolerances, make the DMA buffer much larger.  These are suggestions.
  76. * Please experiment.  If the sound you hear is distorted (i.e., skipping,
  77. * clicking, etc.) or the program hangs, try increasing gDMABufSize.
  78. *
  79. *
  80. * Transfer Rate = Channels * Samples Per Second * Bits Per Sample / 8
  81. *
  82. * --------------------------------------------------------------------------
  83. *    Transfer Rate  |                                         |
  84. *  (Bytes Per Sec.) | Example                                 | gDMABufSize
  85. * --------------------------------------------------------------------------
  86. *         176,400   | 16-bit, STEREO, 44,100 Samples Per Sec. | 16,384 bytes
  87. * --------------------------------------------------------------------------
  88. *          88,200   | 16-bit, MONO,   44,100 Samples Per Sec. |  8,192 bytes
  89. *                   |  8-bit, STEREO, 44,100 Samples Per Sec. |
  90. * --------------------------------------------------------------------------
  91. *          44,100   |  8-bit, MONO,   44,100 Samples Per Sec. |  4,096 bytes
  92. * --------------------------------------------------------------------------
  93. *          11,025   |  8-bit, MONO,   11,025 Samples Per Sec. |  1,024 bytes
  94. * --------------------------------------------------------------------------
  95. *
  96. * The minimum DMA Buffer size required is dependent upon the system
  97. * (i.e., size of disk cache, fragmentation of audio files, CPU speed,
  98. * and disk access time.  It's possible to play 8-bit, MONO, 11,025
  99. * samples-per-second files (with no embedded .VOC block types) with no
  100. * audible degradation with a DMA buffer size of only 256 bytes on a
  101. * 486 system.
  102. *
  103. *************************************************************************/
  104. #define CSPSYS_ERR_NOERROR            0
  105. #define CSPSYS_ERR_GENERALFAILURE     1
  106. #define CSPSYS_ERR_ACQUIRED           2
  107. #define CSPSYS_ERR_UNACQUIRED         3
  108. #define CSPSYS_ERR_UNSUPPORTEDMSG     4
  109. #define CSPSYS_ERR_INVALIDUSER        5
  110. #define CSPSYS_ERR_INVALIDDEVICEID    6
  111. #define CSPSYS_ERR_UNSUPPORTEDPARAM   7
  112. #define CSPSYS_ERR_INVALIDPARAMVALUE  8
  113.  
  114. #define CSPSYS_PARAM_DMAWIDTH      5
  115. #define CSPSYS_PARAM_DRIVERVERSION 1
  116. #define CSPSYS_PARAM_NCHANNELS     4
  117.  
  118. #define CSPSYS_ACQUIRE    1
  119. #define CSPSYS_RELEASE    2
  120. #define CSPSYS_DOWNLOAD   3
  121. #define CSPSYS_START      4
  122. #define CSPSYS_STOP       5
  123. #define CSPSYS_SETPARAM   6
  124. #define CSPSYS_GETPARAM   7
  125.  
  126.  
  127. #define AUTO_INIT                  1
  128. #define BITS_PER_SAMPLE_8     0x0400
  129. #define BITS_PER_SAMPLE_16    0x0800
  130. #define BLOCK_0               0x0001  // Bit 0 = Block Type 0
  131. #define BLOCK_1               0x0002  // Bit 1 = Block Type 1
  132. #define BLOCK_2               0x0004  // Bit 2 = Block Type 2
  133. #define BLOCK_8               0x0100  // Bit 8 = Block Type 8
  134. #define BLOCK_9               0x0200  // Bit 9 = Block Type 9
  135. #define COMMAND_PAUSE           0x01
  136. #define COMMAND_PLAY            0x02
  137. #define COMMAND_QUIT            0x04
  138. #define DMA8_FF_REG           0x000C
  139. #define DMA8_MASK_REG         0x000A
  140. #define DMA8_MODE_REG         0x000B
  141. #define DMA16_FF_REG          0x00D8
  142. #define DMA16_MASK_REG        0x00D4
  143. #define DMA16_MODE_REG        0x00D6
  144. #define DSP_DATA_AVAIL        0x000E
  145. #define DSP_GET_VERSION       0x00E1
  146. #define DSP_READ_PORT         0x000A
  147. #define DSP_READY             0x00AA
  148. #define DSP_RESET_PORT        0x0006
  149. #define DSP_WRITE_PORT        0x000C
  150. #define END_OF_INTERRUPT      0x0020
  151. #define FAIL                       0
  152. #define FALSE                      0
  153. #define FILE_NOT_DONE_PLAYING 0x8000
  154. #define INVALID_BLOCK_TYPE    0x4000
  155. #define INVALID_FILE_FORMAT        1
  156. #define PIC0_COMMAND_REG        0x20
  157. #define PIC1_COMMAND_REG        0xA0
  158. #define PIC0_MASK_REG           0x21
  159. #define PIC1_MASK_REG           0xA1
  160. #define REMEMBER_VOLUME            1
  161. #define RESTORE_VOLUME             2
  162. #define SB2_LO                     1
  163. #define SB2_HI                     2
  164. #define SBPRO                      3
  165. #define SB16                       4
  166. #define SINGLE_CYCLE               2
  167. #define SUCCESS                    1
  168. #define TRUE                  !FALSE
  169. #define UNUSED                     0
  170. #define USE_CSP                        // Comment out if not using CSP chip.
  171. #define VOC_FILE                   2
  172. #define WAVE_FILE                  3
  173.  
  174. #include <conio.h>
  175. #include <ctype.h>
  176. #include <dos.h>
  177. #include <fcntl.h>
  178. #include <io.h>
  179. #include <mem.h>
  180. #include <stdio.h>
  181. #include <stdlib.h>
  182. #include <string.h>
  183. #include <sys\stat.h>
  184.  
  185. /*------------------- TYPEDEFS -----------------------------------------*/
  186. /*----------------------------------------------------------------------*/
  187. typedef struct _BLASTER
  188. {
  189.   short int BaseIOPort,
  190.         DMAChan8Bit,
  191.         DMAChan16Bit,
  192.         DSPVersion,
  193.         IRQNumber,
  194.         MIDIPort;
  195. } BLASTER;
  196.  
  197.  
  198. typedef struct _FILEINFO
  199. {
  200.   unsigned char      BitsPerSample,
  201.              Channels;      // MONO = 1, STEREO = 2
  202.   unsigned short int FileFormat,    // Determines BitsPerSample in old .VOC
  203.              TimeConstant;  // Old .VOC version of SampPerSec
  204.   signed long int    SampPerSec;
  205. } FILEINFO;
  206.  
  207. #ifdef USE_CSP
  208. typedef unsigned long int (far *CSP_FUNC_PTR) (unsigned short int,
  209.                            unsigned short int,
  210.                            unsigned long  int,
  211.                            unsigned long  int,
  212.                            unsigned long  int);
  213. #endif
  214.  
  215.  
  216. typedef unsigned long int DWORD;
  217.  
  218. /*------------------- FUNCTION PROTOTYPES ------------------------------*/
  219. /*----------------------------------------------------------------------*/
  220. char GetBlastEnv(BLASTER *Blast),
  221.      ResetDSP(short int),
  222. #ifdef USE_CSP
  223.      AcquireCSP(CSP_FUNC_PTR, unsigned short int),
  224.      GetCSPDriverAddr(CSP_FUNC_PTR *),
  225.      ReleaseCSP(CSP_FUNC_PTR),
  226. #endif
  227.      VerifyFileType(FILE *);
  228.  
  229.  
  230. short int DSPRead(short int),
  231.       LoadAndPlay(unsigned long int, FILE *, unsigned char *, char
  232. #ifdef USE_CSP
  233.               , CSP_FUNC_PTR*
  234. #endif
  235.              );
  236.  
  237. unsigned long int AllocateDMABuffer(unsigned char **, unsigned short int *),
  238.           OnSamePage(unsigned char *, unsigned short int);
  239.  
  240. void interrupt DmaISR(void);
  241. void ContinueDMA(unsigned char),
  242.      DSPWrite(short int, short int),
  243.      KillVolume(void),
  244.      PauseDMA(unsigned char),
  245.      ProgramDMA(unsigned long int, FILEINFO *, unsigned short int),
  246.      ProgramDSP(unsigned short int, FILEINFO *, unsigned char),
  247.      RestoreOldISR(BLASTER *),
  248.      RestoreOrRememberVolume(char),
  249.      SetDmaISR(BLASTER *),
  250.      SetMixer(void);
  251.  
  252. /*------------------- GLOBAL VARIABLES ---------------------------------*/
  253. /*----------------------------------------------------------------------*/
  254. BLASTER gBlastSet;
  255. #ifdef USE_CSP
  256. char              gCSPAcquired     = FALSE,
  257.          *gCSPUserFileName = "CSPUSER.TMP";
  258. #endif
  259.  
  260. signed short int   gBitsPerSample,
  261.            gChannels;
  262.  
  263.  
  264. unsigned short int gDMABufNowPlaying,
  265.            gDMABufSize,       // See "WARNING #1" above.
  266.            gHighSpeed,
  267.            gIRQMaskSave0,
  268.            gIRQMaskSave1;
  269.  
  270. unsigned long int  gUserNo;
  271.  
  272. signed long int gBytesLeftToPlay;
  273.  
  274. void interrupt (*gOldISRFuncPtr)(void);
  275.  
  276. /*************************************************************************
  277. *
  278. * FUNCTION: main()
  279. *
  280. * DESCRIPTION: READ IT!
  281. *
  282. *************************************************************************/
  283. void main(short int argc, char *argv[])
  284. {
  285.   char               Command,
  286.              KeyPressed;
  287.   FILE              *File;
  288.   short int          Status;
  289.   unsigned char     *DMABuf;
  290.   unsigned long int  DMABufPhysAddr;
  291. #ifdef USE_CSP
  292.   CSP_FUNC_PTR       CSPFunc = 0;
  293. #endif
  294.  
  295.   clrscr();
  296.  
  297.   if (argc != 3)
  298.   {
  299.     puts("Must have at exactly 2 parameters--Filename and DMA buffer size");
  300.     goto exit_0;;
  301.   }
  302.  
  303.   /*--- GET BLASTER ENVIRONMENT VARIABLES AND DSP VERSION --------------*/
  304.   if (GetBlastEnv(&gBlastSet) == FAIL)
  305.   {
  306.     puts("GetBlastEnv() FAILS!");
  307.     goto exit_0;
  308.   }
  309.  
  310.  
  311.   /*--- OPEN THE FILE TO PLAY OR RECORD --------------------------------*/
  312.   printf("Filename = %s\n", argv[1]);
  313.   File = fopen(argv[1], "rb");  // Open file for playing.
  314.   if (File == 0)
  315.   {
  316.     puts("fopen() FAILS!");
  317.     goto exit_0;
  318.   }
  319.  
  320.   /*--- CONVERT COMMAND-LINE PARAMETER TO DMA BUFFER SIZE. -------------*/
  321.   gDMABufSize = (unsigned short int) atoi(argv[2]);
  322.  
  323.  
  324.   /*--- ALLOCATE DMA BUFFER --------------------------------------------*/
  325.   /*--------------------------------------------------------------------*/
  326.   DMABufPhysAddr = AllocateDMABuffer(&DMABuf, &gDMABufSize);
  327.   if (DMABufPhysAddr == FAIL)
  328.   {
  329.     puts("AllocateDMABuffer() FAILS!");
  330.     goto exit_1;
  331.   }
  332.  
  333.     /*--- THE FOLLOWING printf()s ARE FOR DEBUG ONLY! --------------------*/
  334. //  /*
  335.   printf("DMA Buffer = %u bytes.\n", gDMABufSize);
  336.   printf("I/O        = %x (hex)\n",  gBlastSet.BaseIOPort);
  337.   printf("DMA 8-bit  = %d\n",        gBlastSet.DMAChan8Bit);
  338.   printf("DMA 16-bit = %d\n",        gBlastSet.DMAChan16Bit);
  339.   printf("IRQ        = %d\n",        gBlastSet.IRQNumber);
  340.   printf("DSP Ver.   = %d.%02d\n",  (gBlastSet.DSPVersion >> 8) & 0x00FF,
  341.                     (gBlastSet.DSPVersion & 0x00FF));
  342. //  */
  343.  
  344.  
  345.   puts("PRESS:  Space Bar  to Pause/Resume");
  346.   puts("PRESS:  Esc or' Q' to Quit");
  347.  
  348.  
  349.   SetDmaISR(&gBlastSet);
  350.  
  351.   /*--- REMOVE THIS FUNCTION!!!  IT FORCES MIXER VOLUME TO MAXIMUM!!! --*/
  352.   /*--------------------------------------------------------------------*/
  353.   SetMixer();
  354.  
  355.   if (VerifyFileType(File) != VOC_FILE)
  356.   {
  357.     printf("File: %s not a .VOC file--ABORT!\n", argv[1]);
  358.     goto exit_2;
  359.   }
  360.  
  361.   Command = COMMAND_PLAY;  // "Command" MUST equal this to begin playing!
  362.  
  363.   do
  364.   {
  365.     Status = LoadAndPlay(DMABufPhysAddr, File, DMABuf, Command
  366. #ifdef USE_CSP
  367.              , &CSPFunc
  368. #endif
  369.             );
  370.  
  371.     /*--- DETECT KEYS HIT.  PAUSE, RESUME, AND QUIT SUPPORTED. -------*/
  372.     /*----------------------------------------------------------------*/
  373.     if (kbhit())
  374.     {
  375.       KeyPressed = getch();
  376.  
  377.       if (KeyPressed == 0)     // If 1st byte is 0, key pressed sent 2 bytes.
  378.     KeyPressed = getch();  // Flush the 2nd byte from the buffer.
  379.  
  380.       switch(KeyPressed)
  381.       {
  382.     case 32:   // Spacebar
  383.       if (Command == COMMAND_PLAY)        // If playing, pause.
  384.       {
  385.         Command = COMMAND_PAUSE;
  386.  
  387.         if (Status & BITS_PER_SAMPLE_8)
  388.           PauseDMA(8);
  389.         else  // BitsPerSample = 16
  390.           PauseDMA(16);
  391.       }
  392.       else if (Command == COMMAND_PAUSE)  // If paused, continue play.
  393.       {
  394.         Command = COMMAND_PLAY;
  395.  
  396.         if (Status & BITS_PER_SAMPLE_8)
  397.           ContinueDMA(8);
  398.         else
  399.           ContinueDMA(16);  // BitsPerSample == 16
  400.       }
  401.     break;
  402.  
  403.     case 'Q': // 'Q' is for Quit
  404.     case 'q':
  405.     case 27 : // Escape key
  406.       Command = COMMAND_QUIT;
  407.     break;
  408.       }
  409.     }
  410.  
  411.   } while (Status & FILE_NOT_DONE_PLAYING);
  412.  
  413.  
  414. #ifdef USE_CSP
  415.   if (gCSPAcquired == TRUE)
  416.   {
  417.     ReleaseCSP(CSPFunc);
  418.     gCSPAcquired = FALSE;
  419.   }
  420. #endif
  421.  
  422.   /*--- This should never happen, but test for it just in case! --------*/
  423.   /*--------------------------------------------------------------------*/
  424.   if (Status & INVALID_BLOCK_TYPE)
  425.     puts("CRASH!  Unsupported block type detected!");
  426.  
  427.   RestoreOldISR(&gBlastSet);
  428.  
  429. exit_2:
  430.   free(DMABuf);
  431. exit_1:
  432.   fclose(File);
  433. exit_0:
  434.   return;
  435. }
  436.  
  437.  
  438. /*************************************************************************
  439. *
  440. * FUNCTION: LoadAndPlay()
  441. *
  442. * DESCRIPTION:
  443. *
  444. * RETURN: "ReturnStatus", where the bits (bit 0 to bit 15) have the
  445. *         significance shown.
  446. *
  447. *         Bit No.     | If bit set HI (equals 1), it means...
  448. *         ---------------------------------------------------
  449. *          0 (LSBit)  | Block 0 detected (File Done Playing)
  450. *          1          | Block 1 detected
  451. *          2 - 7      | Unused
  452. *          8          | Block 8 detected
  453. *          9          | Block 9 detected
  454. *          10         | BitsPerSample = 8
  455. *          11         | BitsPerSample = 16
  456. *          12 - 13    | Unused
  457. *          14         | Invalid Block Type detected (Fatal Error)
  458. *          15 (MSBit) | File NOT done playing
  459. *
  460. *************************************************************************/
  461. short int LoadAndPlay(unsigned long int   DMABufPhysAddr,
  462.               FILE               *File,
  463.               unsigned char      *DMABuf,
  464.               char                Command
  465. #ifdef USE_CSP
  466.               , CSP_FUNC_PTR*     CSPFunc
  467. #endif
  468.               )
  469. {
  470.   static FILEINFO           FileHdrInfo;
  471.   static signed long int    BlockByteCount;
  472.   static char               FirstTimeFuncCalled = TRUE;
  473.   static unsigned short int Count,
  474.                 DMABufToLoad;
  475.   char                      BlockType;
  476.   short int                 ReturnStatus = FILE_NOT_DONE_PLAYING;
  477.  
  478.   /*--- CHECK FOR COMMANDS SENT AFTER FILE HAS BEGUN PLAYING. ----------*/
  479.   /*--------------------------------------------------------------------*/
  480.   switch(Command)
  481.   {
  482.     case COMMAND_PAUSE:
  483.       if (FileHdrInfo.BitsPerSample == 8)
  484.     ReturnStatus |= BITS_PER_SAMPLE_8;
  485.       else
  486.     ReturnStatus |= BITS_PER_SAMPLE_16;
  487.     return(ReturnStatus);
  488.  
  489.     case COMMAND_PLAY:
  490.       if (FileHdrInfo.BitsPerSample == 8)
  491.     ReturnStatus |= BITS_PER_SAMPLE_8;
  492.       else
  493.     ReturnStatus |= BITS_PER_SAMPLE_16;
  494.     break;
  495.  
  496.     case COMMAND_QUIT:
  497.       ResetDSP(gBlastSet.BaseIOPort); // Stops DMA transfer!
  498.       ReturnStatus &= ~FILE_NOT_DONE_PLAYING;  // File IS DONE playing!
  499.       FirstTimeFuncCalled = TRUE;     // Reset for next file.
  500.     return(ReturnStatus);             // RETURN!
  501.   }
  502.  
  503.  
  504.   if (FirstTimeFuncCalled == TRUE)
  505.   {
  506.     FirstTimeFuncCalled = FALSE;
  507.     goto read_new_block;
  508.   }
  509.   else
  510.   {
  511.     // Prevent loading DMA buffer when 1/2 is loaded and other 1/2 is playing.
  512.     while (DMABufToLoad == gDMABufNowPlaying);  // WAIT!
  513.   }
  514.  
  515.  
  516.   /*--- Point DMABuf to top 1/2 of DMA buffer if needed. ---------------*/
  517.   /*--------------------------------------------------------------------*/
  518.   if (DMABufToLoad == 1)
  519.     DMABuf += (gDMABufSize / 2);  // Load top 1/2 of DMA buffer.
  520.  
  521.   if (BlockByteCount > gDMABufSize / 2)
  522.   {
  523.     /*--- LOAD EXACTLY 1/2 DMA BUFFER FROM THE FILE. -------------------*/
  524.     /*------------------------------------------------------------------*/
  525.     Count = gDMABufSize / 2;
  526.     fread(DMABuf, Count, 1, File);
  527.     DMABufToLoad   ^= 1;             // Get ready to load next 1/2 DMA buffer.
  528.   }
  529.   else if (BlockByteCount >= 0)
  530.   {
  531.     /*--- LOAD ALL DATA REMAINING IN CURRENT BLOCK INTO DMA BUFFER. ----*/
  532.     /*------------------------------------------------------------------*/
  533.     Count = (unsigned short int) BlockByteCount;
  534.     if (Count > 0)
  535.     {
  536.       fread(DMABuf, Count, 1, File);
  537.       if (Count == gDMABufSize / 2)
  538.     DMABufToLoad ^= 1;  // Get ready to load next 1/2 DMA buffer.
  539.  
  540.       ProgramDSP(Count, &FileHdrInfo, SINGLE_CYCLE);
  541.     }
  542.  
  543. read_new_block:
  544.  
  545.     /*--- READ A NEW BLOCK TYPE AND STORE ITS INFORMATION IN THE -----*/
  546.     /*--- THE LOCAL STATIC STRUCTURE "FileHdrInfo".              -----*/
  547.     /*----------------------------------------------------------------*/
  548.     fread(&BlockType, 1, 1, File);
  549.     switch(BlockType)
  550.     {
  551.       case 0:  // TERMINATOR BLOCK
  552.     puts("BLOCK 0");
  553.     while (gBytesLeftToPlay > 0);   // WAIT for buffer to finish playing.
  554.     ReturnStatus |= BLOCK_0;
  555.     ReturnStatus &= ~FILE_NOT_DONE_PLAYING;  // File IS DONE playing!
  556.     FirstTimeFuncCalled = TRUE;     // Reset for next file.
  557.       break;
  558.  
  559.  
  560.       case 1:  // DIGITIZED SOUND BLOCK
  561.     puts("BLOCK 1");
  562.     BlockByteCount = 0L;  // Clears MSByte
  563.     fread(&BlockByteCount,           3, 1, File);
  564.     fread(&FileHdrInfo.TimeConstant, 1, 1, File);
  565.     fread(&FileHdrInfo.FileFormat,   1, 1, File);
  566.     BlockByteCount           -= 2;
  567.     FileHdrInfo.Channels      = 1;  // MONO
  568.     FileHdrInfo.BitsPerSample = 8;
  569.     FileHdrInfo.SampPerSec    = -1000000L /
  570.                     ((long) FileHdrInfo.TimeConstant - 256L);
  571.     ReturnStatus |= BLOCK_1;
  572.       break;
  573.  
  574.  
  575.       case 8:  // DIGITIZED SOUND BLOCK
  576.     puts("BLOCK 8");
  577.     fseek(File, 3, SEEK_CUR);        // Skip Block 8 BlockLen field.
  578.     fread(&FileHdrInfo.TimeConstant, 2, 1, File);
  579.     fread(&FileHdrInfo.FileFormat,   1, 1, File);
  580.     fread(&FileHdrInfo.Channels,     1, 1, File);
  581.     FileHdrInfo.Channels++;          // Make MONO = 1, STEREO = 2
  582.     FileHdrInfo.TimeConstant >>= 8;  // Only HI byte is used.
  583.  
  584.  
  585.     /*--- BLOCK TYPE 8 IS IMMEDIATELY FOLLOWED BY BLOCK TYPE 1. --*/
  586.     fseek(File, 1, SEEK_CUR);            // Skip Block Type 1 ID.
  587.     BlockByteCount = 0L;                 // Clears MSByte
  588.     fread(&BlockByteCount, 3, 1, File);
  589.     fseek(File, 2, SEEK_CUR);            // Skip next 2 bytes
  590.     BlockByteCount -= 2;
  591.  
  592.     FileHdrInfo.BitsPerSample = 8;
  593.     FileHdrInfo.SampPerSec    = (-1000000L /
  594.                     ((long) FileHdrInfo.TimeConstant - 256L))
  595.                     / FileHdrInfo.Channels;
  596.  
  597.     ReturnStatus |= BLOCK_8;
  598.       break;
  599.  
  600.  
  601.       case 9:  // DIGITIZED SOUND BLOCK
  602.     puts("BLOCK 9");
  603.     BlockByteCount = 0L;  // Clears MSByte
  604.     fread(&BlockByteCount,            3, 1, File);
  605.     fread(&FileHdrInfo.SampPerSec,    4, 1, File);
  606.     fread(&FileHdrInfo.BitsPerSample, 1, 1, File);
  607.     fread(&FileHdrInfo.Channels,      1, 1, File);
  608.     fread(&FileHdrInfo.FileFormat,    2, 1, File);
  609.     fseek(File, 4, SEEK_CUR);  // Skip reserved bytes.
  610.     BlockByteCount -= 12;
  611.     FileHdrInfo.TimeConstant = (unsigned short int) ((256L - 1000000L)
  612.                    / ((long) FileHdrInfo.Channels *
  613.                    FileHdrInfo.SampPerSec) & 0x00FF);
  614.     ReturnStatus |= BLOCK_9;
  615.       break;
  616.  
  617.       default:
  618.     ReturnStatus |= INVALID_BLOCK_TYPE;
  619.     ReturnStatus &= ~FILE_NOT_DONE_PLAYING;  // File IS DONE playing!
  620.     FirstTimeFuncCalled = TRUE;              // Reset for next file.
  621.       break;
  622.     }
  623.  
  624.     switch (BlockType)
  625.     {
  626.       case 0: // Blocktype is block terminator.
  627.     /*--- THE FOLLOWING printf()s ARE FOR DEBUG ONLY. --------------*/
  628.     /*--------------------------------------------------------------*/
  629.     printf("BlockType     = %d\n",  BlockType);
  630.     printf("BitsPerSample = %u\n",  FileHdrInfo.BitsPerSample);
  631.     printf("Channels      = %u\n",  FileHdrInfo.Channels);
  632.     printf("FileFormat    = %u\n",  FileHdrInfo.FileFormat);
  633.     printf("TimeConstant  = %u\n",  FileHdrInfo.TimeConstant);
  634.     printf("SampPerSec    = %ld\n", FileHdrInfo.SampPerSec);
  635.       break;
  636.  
  637.  
  638.       case 1:  // Block types 1, 8, 9 are digital sound blocks.
  639.       case 8:
  640.       case 9:
  641.     ResetDSP(gBlastSet.BaseIOPort);  // Always avoid problems--reset!
  642.     gBitsPerSample = FileHdrInfo.BitsPerSample;
  643.     gChannels      = FileHdrInfo.Channels;
  644.  
  645. #ifdef USE_CSP
  646.     /*--- GET CSP DRIVER ADDRESS AND LOAD CSP CODE. ----------------*/
  647.     /*--------------------------------------------------------------*/
  648.     if (gCSPAcquired == FALSE)
  649.     {
  650.       switch(FileHdrInfo.FileFormat)
  651.       {
  652.         case 0x0006:  // A-LAW     16-bit 2:1 compression
  653.         case 0x0007:  // MU-LAW    16-bit 2:1 compression
  654.         case 0x0200:  // CT-ADPCM  16-bit 4:1 compression
  655.           if (GetCSPDriverAddr(CSPFunc) == FAIL)
  656.           {
  657.         puts("Can't open CSP.SYS driver.  ABORT!");
  658.         ReturnStatus &= ~FILE_NOT_DONE_PLAYING;
  659.         return(ReturnStatus);
  660.           }
  661.  
  662.           AcquireCSP(*CSPFunc, FileHdrInfo.FileFormat);
  663.           gCSPAcquired = TRUE;
  664.         break;
  665.       }
  666.     }
  667. #endif
  668.  
  669.     DMABufToLoad      = 1;     // Next 1/2 DMA buffer to load is top 1/2.
  670.     gBytesLeftToPlay  = BlockByteCount;  // Altered by DmaISR().
  671.     gDMABufNowPlaying = 0;     // Altered by ISR when 1/2 buffer done playing.
  672.     gHighSpeed        = FALSE; // Initialize to NOT high-speed DMA.
  673.  
  674.     /*--- LOAD DMA BUFFER AND BEGIN PLAYING THE FILE. --------*/
  675.     /*--------------------------------------------------------*/
  676.     ProgramDMA(DMABufPhysAddr, &FileHdrInfo, gDMABufSize);
  677.  
  678.     if (BlockByteCount > gDMABufSize / 2)
  679.     {
  680.       Count = gDMABufSize / 2;
  681.       fread(DMABuf, Count, 1, File);
  682.       ProgramDSP(Count, &FileHdrInfo, AUTO_INIT);  // Begin audio.
  683.     }
  684.     else
  685.     {
  686.       Count = (int) BlockByteCount;
  687.       fread(DMABuf, Count, 1, File);
  688.       ProgramDSP(Count, &FileHdrInfo, SINGLE_CYCLE);  // Begin audio.
  689.     }
  690.       break;
  691.     }
  692.  
  693.   } // End: else if (BlockByteCount >= 0)
  694.  
  695.   BlockByteCount -= (long) Count;  // Update No. of bytes left in block.
  696.  
  697.   return(ReturnStatus);
  698. }
  699.  
  700. /*************************************************************************
  701. *
  702. * FUNCTION: ProgramDMA()
  703. *
  704. * DESCRIPTION:  This function programs the DMA chip to use a single
  705. *               8-bit or 16-bit DMA channel (specified by the BLASTER
  706. *               environment string) for audio transfer.  It also programs
  707. *               the size of the DMA transfer and the DMA buffer address
  708. *               used for the audio transfer.
  709. *
  710. *************************************************************************/
  711. void ProgramDMA(unsigned long int DMABufPhysAddr, FILEINFO *FileHdrInfo,
  712.         unsigned short int Count)
  713. {
  714.   short int Command,
  715.         DMAAddr,
  716.         DMACount,
  717.         DMAPage,
  718.         Offset,
  719.         Page,
  720.         Temp;
  721.  
  722.   Page   = (short int) (DMABufPhysAddr >> 16);
  723.   Offset = (short int) (DMABufPhysAddr & 0xFFFF);
  724.  
  725.   if (FileHdrInfo->FileFormat < 4)  // 8-BIT FILE
  726.   {
  727.     switch(gBlastSet.DMAChan8Bit)
  728.     {
  729.       case 0:
  730.     DMAAddr  = 0x0000;
  731.     DMACount = 0x0001;
  732.     DMAPage  = 0x0087;
  733.       break;
  734.  
  735.       case 1:
  736.     DMAAddr  = 0x0002;
  737.     DMACount = 0x0003;
  738.     DMAPage  = 0x0083;
  739.       break;
  740.  
  741.       case 3:
  742.     DMAAddr  = 0x0006;
  743.     DMACount = 0x0007;
  744.     DMAPage  = 0x0082;
  745.       break;
  746.     }
  747.  
  748.     outp(DMA8_MASK_REG, gBlastSet.DMAChan8Bit | 4);    // Disable DMA
  749.     outp(DMA8_FF_REG, 0x0000);                         // Clear F-F
  750.     outp(DMA8_MODE_REG, gBlastSet.DMAChan8Bit | 0x58); // 8-bit AI
  751.     outp(DMACount, ((Count - 1) & 0xFF));              // LO byte
  752.     outp(DMACount, ((Count - 1) >> 8));                // HI byte
  753.   }
  754.   else  // 16-BIT FILE
  755.   {
  756.     switch(gBlastSet.DMAChan16Bit)
  757.     {
  758.       case 5:
  759.     DMAAddr  = 0x00C4;
  760.     DMACount = 0x00C6;
  761.     DMAPage  = 0x008B;
  762.       break;
  763.  
  764.       case 6:
  765.     DMAAddr  = 0x00C8;
  766.     DMACount = 0x00CA;
  767.     DMAPage  = 0x0089;
  768.       break;
  769.  
  770.       case 7:
  771.     DMAAddr  = 0x00CC;
  772.     DMACount = 0x00CE;
  773.     DMAPage  = 0x008A;
  774.       break;
  775.     }
  776.  
  777.     // Offset for 16-bit DMA must be calculated different than 8-bit.
  778.     // Shift Offset 1 bit right.  Then copy LSBit of Page to MSBit of Offset.
  779.     Temp = Page & 0x0001;  // Get LSBit of Page and...
  780.     Temp <<= 15;           // move it to MSBit of Temp.
  781.     Offset >>= 1;          // Divide Offset by 2.
  782.     Offset &= 0x7FFF;      // Clear MSBit of Offset.
  783.     Offset |= Temp;        // Put LSBit of Page into MSBit of Offset.
  784.  
  785.     outp(DMA16_MASK_REG, (gBlastSet.DMAChan16Bit - 4) | 4);    // Disable DMA
  786.     outp(DMA16_FF_REG, 0x0000) ;                               // Clear F-F
  787.     outp(DMA16_MODE_REG, (gBlastSet.DMAChan16Bit - 4) | 0x58); // 16-bit AI
  788.     outp(DMACount, ((Count/2 - 1) & 0xFF));                    // LO byte
  789.     outp(DMACount, ((Count/2 - 1) >> 8));                      // HI byte
  790.   }
  791.  
  792.   // Program the starting address of the DMA buffer.
  793.   outp(DMAPage, Page);             // Page number of DMA buffer.
  794.   outp(DMAAddr, Offset & 0x00FF);  // LO byte offset address of DMA buffer.
  795.   outp(DMAAddr, (Offset >> 8));    // HI byte offset address of DMA buffer.
  796.  
  797.   // Reenable 8-bit or 16-bit DMA.
  798.   if (FileHdrInfo->FileFormat < 4)
  799.     outp(DMA8_MASK_REG,  gBlastSet.DMAChan8Bit);
  800.   else
  801.     outp(DMA16_MASK_REG, gBlastSet.DMAChan16Bit - 4);
  802.  
  803.   return;
  804. }
  805.  
  806.  
  807. /*************************************************************************
  808. *
  809. * FUNCTION: ProgramDSP()
  810. *
  811. * DESCRIPTION: This function programs the DSP chip on the Sound Blaster
  812. *              card.  The card type is identified by the DSP version
  813. *              number.  Each type of Sound Blaster card is programmed
  814. *              differently unless an 8-bit ADPCM file is played.  In that
  815. *              case, all SB cards are programmed identically.
  816. *
  817. *************************************************************************/
  818. void ProgramDSP(unsigned short int Count, FILEINFO *FileHdrInfo,
  819.         unsigned char DMAMode)
  820. {
  821.   unsigned char Card;
  822.   short int     Command,
  823.         Mode;
  824.  
  825.   if (gHighSpeed == TRUE)  // Once in high-speed mode, DSP can only be reset!
  826.     return;
  827.  
  828.   // Make sure Count is >= 2, so when the DSP is programmed for a block
  829.   // tranfer, Count doesn't wrap around to a large number when 1 is
  830.   // subtracted from it.
  831.   if (Count <= 1)
  832.     Count = 2;
  833.  
  834.  
  835.   /*--- DETERMINE SOUND BLASTER CARD TYPE ----------------------------*/
  836.   /*------------------------------------------------------------------*/
  837.   if (gBlastSet.DSPVersion >= 0x0400)       // DSP version >= 4.00
  838.     Card = SB16;
  839.   else if (gBlastSet.DSPVersion >= 0x0300)  // DSP version = 3.xx
  840.   {
  841.     // Set SBPRO mixer register default to MONO, Output filter to OFF.
  842.     outp(gBlastSet.BaseIOPort + 4, 0x000E);  // Select mixer reg. 0x0E.
  843.     outp(gBlastSet.BaseIOPort + 5, 0x0000);  // MONO, Output filter off.
  844.     Card = SBPRO;
  845.   }
  846.   else if (gBlastSet.DSPVersion >= 0x0201)  // 2.01 <= DSP version < 3.00
  847.     Card = SB2_HI;
  848.   else if (gBlastSet.DSPVersion == 0x0200)  // DSP version = 2.00
  849.     Card = SB2_LO;
  850.  
  851.  
  852.   /*--- FILE IS 8-BIT ADPCM PLAYBACK. IN THIS CASE, ALL SB CARDS  ------*/
  853.   /*--- ARE PROGRAMMED IDENTICALLY.  DETERMINE THE COMMAND.       ------*/
  854.   /*--------------------------------------------------------------------*/
  855.   if (FileHdrInfo->FileFormat > 0 && FileHdrInfo->FileFormat < 4)
  856.   {
  857.     switch(FileHdrInfo->FileFormat)
  858.     {
  859.       case 1:  // 8-bit 2:1 compression (4-bit ADPCM)
  860.     Command = 0x0074;  // default to single-cycle
  861.       break;
  862.  
  863.       case 2:  // 8-bit 8:3 compression (2.6-bit ADPCM)
  864.     Command = 0x0076;  // default to single-cycle
  865.       break;
  866.  
  867.       case 3:  // 8-bit 4:1 compression (2-bit ADPCM)
  868.     Command = 0x0016;  // default to single-cycle
  869.       break;
  870.     }
  871.  
  872.     if (DMAMode == AUTO_INIT)
  873.       Command |= 0x0009;   // Set to auto-init mode.
  874.   }
  875.   else
  876.   {
  877.     /*--- FILE IS 8-BIT OR 16-BIT UNCOMPRESSED AUDIO.  PROGRAM EACH ----*/
  878.     /*--- SOUND BLASTER CARD DIFFERENTLY.                           ----*/
  879.     /*------------------------------------------------------------------*/
  880.     switch(Card)
  881.     {
  882.       case SB16:
  883.     // Program sample rate HI and LO byte.
  884.     DSPWrite(gBlastSet.BaseIOPort, 0x0041);
  885.     DSPWrite(gBlastSet.BaseIOPort, (FileHdrInfo->SampPerSec & 0xFF00) >> 8);
  886.     DSPWrite(gBlastSet.BaseIOPort, (FileHdrInfo->SampPerSec & 0xFF));
  887.  
  888.     /*--- DETERMINE 8-bit OR 16-bit, MONO OR STEREO ----------------*/
  889.     /*--------------------------------------------------------------*/
  890.     if (FileHdrInfo->BitsPerSample == 8)
  891.     {
  892.       Command = 0x00C0;  // 8-bit transfer (default: single-cycle, D/A)
  893.  
  894.       if (FileHdrInfo->Channels == 1)
  895.         Mode = 0x0000;   // MONO, unsigned PCM data
  896.       else
  897.         Mode = 0x0020;   // STEREO, unsigned PCM data
  898.     }
  899.     else  // 16-BIT AUDIO
  900.     {
  901.       Command = 0x00B0;  // 16-bit transfer (default: single-cycle, D/A)
  902.       Count  /= 2;       // Set Count to transfer 16-bit words.
  903.  
  904.       if (FileHdrInfo->Channels == 1)
  905.         Mode = 0x0010;   // MONO, signed PCM data
  906.       else
  907.         Mode = 0x0030;   // STEREO, signed PCM data
  908.     }
  909.  
  910.     /*--- CHANGE COMMAND TO AUTO-INIT, IF NEEDED. ------------------*/
  911.     /*--------------------------------------------------------------*/
  912.     if (DMAMode == AUTO_INIT)
  913.       Command |= 0x0004;     // Auto-init
  914.  
  915.  
  916.     /*--- PROGRAM THE DSP CHIP (BEGIN DMA TRANSFER) AND RETURN! ----*/
  917.     /*--------------------------------------------------------------*/
  918.     DSPWrite(gBlastSet.BaseIOPort, Command);
  919.     DSPWrite(gBlastSet.BaseIOPort, Mode);
  920.     DSPWrite(gBlastSet.BaseIOPort, (Count - 1) & 0xFF);  // LO byte
  921.     DSPWrite(gBlastSet.BaseIOPort, (Count - 1) >> 8);    // HI byte
  922.       return;    // RETURN!
  923.  
  924.  
  925.       case SBPRO:
  926.     DSPWrite(gBlastSet.BaseIOPort, 0x00A0);  // Default to MONO.
  927.  
  928.     if (FileHdrInfo->Channels == 2)
  929.     {
  930.       // HI-SPEED, STEREO
  931.       gHighSpeed = TRUE;
  932.  
  933.       DSPWrite(gBlastSet.BaseIOPort, 0x00A8);  // STEREO MODE
  934.       outp(gBlastSet.BaseIOPort + 4, 0x000E);  // Select mixer reg. 0x0E.
  935.       outp(gBlastSet.BaseIOPort + 5, 0x0002);  // STEREO, output filter off.
  936.  
  937.       if (DMAMode == AUTO_INIT)
  938.         Command = 0x0090;  // HIGH-SPEED, AUTO-INIT MODE
  939.       else
  940.         Command = 0x0091;  // HIGH-SPEED, SINGLE-CYCLE MODE
  941.     }
  942.     else if (FileHdrInfo->SampPerSec >= 23000)
  943.     {
  944.       // HI-SPEED, MONO
  945.       gHighSpeed = TRUE;
  946.  
  947.       if (DMAMode == AUTO_INIT)
  948.         Command = 0x0090;  // HIGH-SPEED, AUTO-INIT MODE
  949.       else
  950.         Command = 0x0091;  // HIGH-SPEED, SINGLE-CYCLE MODE
  951.     }
  952.     else if (DMAMode == AUTO_INIT)
  953.       Command = 0x001C;   // NORMAL, AUTO-INIT
  954.     else
  955.       Command = 0x0014;   // NORMAL, SINGLE-CYCLE
  956.       break;
  957.  
  958.  
  959.       case SB2_HI:
  960.     if (FileHdrInfo->SampPerSec > 13000 || FileHdrInfo->Channels == 2)
  961.     {
  962.       // HI-SPEED
  963.       gHighSpeed = TRUE;
  964.  
  965.       if (DMAMode == AUTO_INIT)
  966.         Command = 0x0090;  // HIGH-SPEED, AUTO-INIT MODE
  967.       else
  968.         Command = 0x0091;  // HIGH-SPEED, SINGLE-CYCLE MODE
  969.     }
  970.     else if (DMAMode == AUTO_INIT)
  971.       Command = 0x001C;  // NORMAL, MONO, AUTO-INIT
  972.     else
  973.       Command = 0x0014;  // NORMAL, MONO, SINGLE-CYCLE
  974.       break;
  975.  
  976.  
  977.       case SB2_LO:  // DSP VERSION == 2.00.  HIGH-SPEED MODE NOT AVAILABLE.
  978.     if (DMAMode == AUTO_INIT)
  979.       Command = 0x001C;  // NORMAL, MONO, AUTO-INIT
  980.     else
  981.       Command = 0x0014;  // NORMAL, MONO, SINGLE-CYCLE
  982.       break;
  983.     }
  984.   }
  985.  
  986.  
  987.   /*--- IF FILE IS 8-BIT ADPCM (REGARDLESS OF CARD TYPE), OR CARD IS ---*/
  988.   /*--- AN 8-BIT AUDIO CARD (DSP VERSION < 4.xx), BEGIN DMA TRANFER. ---*/
  989.   /*--------------------------------------------------------------------*/
  990.   DSPWrite(gBlastSet.BaseIOPort, 0x00D1);  // Turn speaker on.
  991.   DSPWrite(gBlastSet.BaseIOPort, 0x0040);  // Program Time Constant
  992.   DSPWrite(gBlastSet.BaseIOPort, FileHdrInfo->TimeConstant);
  993.  
  994.   /*--- NOTE: If in high-speed mode, single-cycle DMA is programmed ----*/
  995.   /*--- using the same initial DSP command as auto-init (0x0048).   ----*/
  996.   /*--------------------------------------------------------------------*/
  997.   if (DMAMode == AUTO_INIT || gHighSpeed == TRUE)
  998.   {
  999.     // Program block tranfer size LO and HI byte and begin tranfer.
  1000.     DSPWrite(gBlastSet.BaseIOPort, 0x0048);
  1001.     DSPWrite(gBlastSet.BaseIOPort, (Count - 1) & 0x00FF);  // LO byte
  1002.     DSPWrite(gBlastSet.BaseIOPort, (Count - 1) >> 8);      // HI byte
  1003.     DSPWrite(gBlastSet.BaseIOPort, Command);               // Begin Xfer
  1004.   }
  1005.   else  // DMAMode == SINGLE_CYCLE  If mode is high-speed, execute above code.
  1006.   {
  1007.     // Program size of last block and begin transfer.
  1008.     DSPWrite(gBlastSet.BaseIOPort, Command);
  1009.     DSPWrite(gBlastSet.BaseIOPort, (Count - 1) & 0x00FF);  // LO byte
  1010.     DSPWrite(gBlastSet.BaseIOPort, (Count - 1) >> 8);      // HI byte
  1011.   }
  1012.  
  1013.   return;
  1014. }
  1015.  
  1016.  
  1017. /*************************************************************************
  1018. *
  1019. * FUNCTION: VerifyFileType()
  1020. *
  1021. * DESCRIPTION:  1) Verifies that the file represented by the file pointer
  1022. *                  passed to this function is either WAVE, Creative VOC,
  1023. *                  or invalid.  FileType is returned.
  1024. *
  1025. *************************************************************************/
  1026. char VerifyFileType(FILE *File)
  1027. {
  1028.   char               Buffer[20],
  1029.              FileType;
  1030.   unsigned short int VocIDCode,
  1031.              VocOffset,
  1032.              VocVersion;
  1033.  
  1034.   //--- DETERMINE IF FILE IS VOC, WAVE, OR INVALID ---------------------
  1035.   fread(Buffer, 20, 1, File);
  1036.  
  1037.   if (!memcmp(Buffer, "Creative Voice File", 19) && Buffer[19] == 0x1A)
  1038.   {
  1039.     fread(&VocOffset,  2, 1, File);
  1040.     fread(&VocVersion, 2, 1, File);
  1041.     fread(&VocIDCode,  2, 1, File);
  1042.  
  1043.     if ((~VocVersion + 0x1234) == VocIDCode)  // Verify that file is VOC.
  1044.     {
  1045.       FileType = VOC_FILE;
  1046.       fseek(File, (long) VocOffset, SEEK_SET);  // Go to 1st Data Block
  1047.     }
  1048.   }
  1049.   else if (!memcmp(Buffer, "RIFF", 4) && !memcmp(&Buffer[8], "WAVEfmt ", 8))
  1050.     FileType = WAVE_FILE;
  1051.   else
  1052.     FileType = INVALID_FILE_FORMAT;
  1053.  
  1054.   return(FileType);
  1055. }
  1056.  
  1057.  
  1058. /*************************************************************************
  1059. *
  1060. * FUNCTION:  DmaISR()
  1061. *
  1062. * DESCRIPTION: If the interrupt was a DMA generated by the Sound Blaster,
  1063. *              acknowledge the interrupt, update the global variables,
  1064. *              and send the end-of-interrupt command(s).
  1065. *
  1066. *              If the interrupt was NOT generated by the Sound Blaster,
  1067. *              call the ISR (gOldISRFuncPtr) saved by SetDMAISR() or
  1068. *              return.
  1069. *
  1070. *************************************************************************/
  1071. void interrupt DmaISR(void)
  1072. {
  1073.   unsigned char InterruptStatus;
  1074.  
  1075.   // Read Sound Blaster mixer interrupt register to determine interrupt type.
  1076.   outp(gBlastSet.BaseIOPort + 4, 0x0082);           // Select interrupt reg.
  1077.   InterruptStatus = inp(gBlastSet.BaseIOPort + 5);  // Read interrupt reg.
  1078.  
  1079.   if (InterruptStatus & 0x01)            // Interrupt is from 8-bit DMA.
  1080.     inp(gBlastSet.BaseIOPort + 0x000E);  // Acknowledge the interrupt.
  1081.   else if (InterruptStatus & 0x02)       // Interrupt is from 16-bit DMA.
  1082.     inp(gBlastSet.BaseIOPort + 0x000F);  // Acknowledge the interrupt.
  1083.   else
  1084.   {
  1085.     // Interrupt is NOT SB DMA.  Call ISR saved by SetDmaISR() or return.
  1086.     if (gOldISRFuncPtr)
  1087.       (*gOldISRFuncPtr)();
  1088.     return;
  1089.   }
  1090.  
  1091.   gBytesLeftToPlay  -= (long) (gDMABufSize / 2);
  1092.   gDMABufNowPlaying ^= 1;  // Keep track of which 1/2 DMA buffer is playing.
  1093.  
  1094.   // Send end-of-interrupt command(s).
  1095.   if (gBlastSet.IRQNumber > 7)
  1096.     outp(PIC1_COMMAND_REG, END_OF_INTERRUPT);
  1097.  
  1098.   outp(PIC0_COMMAND_REG, END_OF_INTERRUPT);
  1099.  
  1100.   return;
  1101. }
  1102.  
  1103.  
  1104. /*************************************************************************
  1105. *
  1106. * FUNCTION: RestoreOldISR()
  1107. *
  1108. * DESCRIPTION:  1) Disable all interrupts.
  1109. *               2) Restore IRQ mask for IRQs 0 to 7.
  1110. *               3) If necessary, restore IRQ mask for IRQs 8 to 15.
  1111. *               4) Restore the original ISR (which was saved to a global
  1112. *                  varaiable function pointer in SetDmaISR()) for the
  1113. *                  interrupt vector number associated with
  1114. *                  BlastSet->IRQNumber.
  1115. *               5) Enable all interrupts.
  1116. *
  1117. *************************************************************************/
  1118. void RestoreOldISR(BLASTER *BlastSet)
  1119. {
  1120.   short int IntVectorNumber;
  1121.  
  1122.   disable();  // Temporarily disable interrupts
  1123.  
  1124.   outp(PIC0_MASK_REG, gIRQMaskSave0);    // Restore IRQ mask for IRQs 0 to 7.
  1125.  
  1126.   if (BlastSet->IRQNumber > 7)
  1127.   {
  1128.     outp(PIC1_MASK_REG, gIRQMaskSave1);  // Restore IRQ mask for IRQs 8 to 15.
  1129.     IntVectorNumber = BlastSet->IRQNumber - 8 + 0x70;
  1130.   }
  1131.   else  // BlastSet->IRQNumber is 0 to 7.
  1132.     IntVectorNumber = BlastSet->IRQNumber + 8;
  1133.  
  1134.   // Restore the old ISR to the interrupt vector number.
  1135.   setvect(IntVectorNumber, gOldISRFuncPtr);
  1136.  
  1137.   enable();   // Enable interrupts
  1138.   return;
  1139. }
  1140.  
  1141.  
  1142. /*************************************************************************
  1143. *
  1144. * FUNCTION: SetDmaISR()
  1145. *
  1146. * DESCRIPTION:  1) Disable all interrupts.
  1147. *               2) Save current interrupt mask(s) to global variable(s).
  1148. *               3) Set new interrupt mask(s).
  1149. *               4) Save ISR associated with BlastSet->IRQNumber to
  1150. *                  a global variable function pointer.
  1151. *               5) Set the new ISR associated with BlastSet->IRQNumber.
  1152. *               6) Enable all interrupts.
  1153. *
  1154. *************************************************************************/
  1155. void SetDmaISR(BLASTER *BlastSet)
  1156. {
  1157.   short int IntVectorNum,
  1158.         IRQMaskNew;
  1159.  
  1160.   disable();  // Temporarily disable interrupts.
  1161.  
  1162.   /*--- Save current interrupt masks and set the new ones. -------------*/
  1163.   gIRQMaskSave0 = inp(PIC0_MASK_REG);    // Save IRQ 0 to 7 mask.
  1164.   if (BlastSet->IRQNumber > 7)
  1165.   {
  1166.     IntVectorNum  = BlastSet->IRQNumber - 8 + 0x70;
  1167.     gIRQMaskSave1 = inp(PIC1_MASK_REG);  // Save IRQ 8 to 15 mask.
  1168.  
  1169.     // Set new IRQ mask for IRQs 8 to 15.
  1170.     IRQMaskNew = ~(((short int) 0x0001) << (BlastSet->IRQNumber - 8));
  1171.     outp(PIC1_MASK_REG, gIRQMaskSave1 & IRQMaskNew);
  1172.  
  1173.     // Setting IRQ mask 2 on PIC 0 enables IRQs 8 to 15 on PIC 1.
  1174.     outp(PIC0_MASK_REG, gIRQMaskSave0 & ~0x0004);
  1175.   }
  1176.   else  // BlastSet->IRQNumber is 0 to 7.
  1177.   {
  1178.     IntVectorNum = BlastSet->IRQNumber + 8;
  1179.  
  1180.     // Set new IRQ mask for IRQs 0 to 7.
  1181.     IRQMaskNew = ~(((short int) 0x0001) << BlastSet->IRQNumber);
  1182.     outp(PIC0_MASK_REG, gIRQMaskSave0 & IRQMaskNew);
  1183.   }
  1184.  
  1185.   /*--- Save current ISR and set the new one. --------------------------*/
  1186.   gOldISRFuncPtr = getvect(IntVectorNum);
  1187.   setvect(IntVectorNum, DmaISR);
  1188.  
  1189.   enable();  // Enable interrupts.
  1190.  
  1191.   return;
  1192. }
  1193.  
  1194.  
  1195. /************************************************************************
  1196. *
  1197. * FUNCTION: GetBlastEnv()
  1198. *
  1199. * DESCRIPTION: Search the BLASTER environment string for:
  1200. *
  1201. *              A) Base IO Port Address
  1202. *              B) LO (8 bit)  DMA Channel
  1203. *              C) HI (16 bit) DMA Channel
  1204. *              D) IRQ Number
  1205. *              E) MIDI Port address
  1206. *
  1207. *              The Base I/O port address and the MIDI address are stored
  1208. *              in the environment string in hex--convert them to integer.
  1209. *              These numbers from the environment string are then placed
  1210. *              in the BLASTER struct passed to this function as a pointer.
  1211. *              The BLASTER struct is defined as:
  1212. *
  1213. *              typedef struct _BLASTER
  1214. *              {
  1215. *                short int BaseIOPort,
  1216. *                          DMAChan8Bit,
  1217. *                          DMAChan16Bit,
  1218. *                          DSPVersion,
  1219. *                          IRQNumber,
  1220. *                          MIDIPort;
  1221. *              } BLASTER;
  1222. *
  1223. *              Then, get the DSP version of the Sound Blaster DSP chip.
  1224. *              This is used to determine the Sound Blaster's capabilities.
  1225. *
  1226. * RETURN: FAIL - BLASTER environment string is not found or any of
  1227. *                the BLASTER structure members aren't found.
  1228. *
  1229. *         SUCCESS - All 5 members of BLASTER struct are found in the
  1230. *                   BLASTER environment string.
  1231. *
  1232. ************************************************************************/
  1233. char GetBlastEnv(BLASTER *Blast)
  1234. {
  1235.   char  Buffer[5],
  1236.     DMAChannelNotFound = TRUE,
  1237.        *EnvString,
  1238.     IOPortNotFound     = TRUE,
  1239.     IRQNotFound        = TRUE,
  1240.     SaveChar;
  1241.  
  1242.   short int digit,
  1243.         i,
  1244.         Major,
  1245.         Minor,
  1246.         multiplier;
  1247.  
  1248.  
  1249.   EnvString = getenv("BLASTER");
  1250.  
  1251.   if (EnvString == NULL)
  1252.     return(FAIL);  // BLASTER environment variable not found.
  1253.  
  1254.   do
  1255.   {
  1256.     switch(*EnvString)
  1257.     {
  1258.       case 'A':  // I/O base port address found
  1259.       case 'a':
  1260.     EnvString++;
  1261.     for (i = 0; i < 3; i++)  // Grab the digits
  1262.     {
  1263.       Buffer[i] = *EnvString;
  1264.       EnvString++;
  1265.     }
  1266.  
  1267.     // The string is in ASCII HEX, convert it to decimal
  1268.     multiplier = 1;
  1269.     Blast->BaseIOPort = 0;
  1270.     for (i -= 1; i >= 0; i--)
  1271.     {
  1272.       // Convert to HEX
  1273.       if (Buffer[i] >= '0' && Buffer[i] <= '9')
  1274.         digit = Buffer[i] - '0';
  1275.       else if (Buffer[i] >= 'A' && Buffer[i] <= 'F')
  1276.         digit = Buffer[i] - 'A' + 10;
  1277.       else if (Buffer[i] >= 'a' && Buffer[i] <= 'f')
  1278.         digit = Buffer[i] - 'a' + 10;
  1279.  
  1280.       Blast->BaseIOPort += digit * multiplier;
  1281.       multiplier *= 16;
  1282.     }
  1283.  
  1284.     IOPortNotFound = FALSE;
  1285.       break;
  1286.  
  1287.  
  1288.       case 'D': // 8-bit DMA channel
  1289.       case 'd':
  1290.       case 'H': // 16-bit DMA channel
  1291.       case 'h':
  1292.     SaveChar = *EnvString;
  1293.     EnvString++;
  1294.     Buffer[0] = *EnvString;
  1295.     EnvString++;
  1296.  
  1297.     if (*EnvString >= '0' && *EnvString <= '9')
  1298.     {
  1299.       Buffer[1] = *EnvString; // DMA Channel No. is 2 digits
  1300.       Buffer[2] = NULL;
  1301.       EnvString++;
  1302.     }
  1303.     else
  1304.       Buffer[1] = NULL;       // DMA Channel No. is 1 digit
  1305.  
  1306.     if (SaveChar == 'D' || SaveChar == 'd')
  1307.       Blast->DMAChan8Bit  = atoi(Buffer);  // 8-Bit DMA channel
  1308.     else
  1309.       Blast->DMAChan16Bit = atoi(Buffer);  // 16-bit DMA channel
  1310.  
  1311.     DMAChannelNotFound = FALSE;
  1312.       break;
  1313.  
  1314.  
  1315.       case 'I':  // IRQ number
  1316.       case 'i':
  1317.     EnvString++;
  1318.     Buffer[0] = *EnvString;
  1319.     EnvString++;
  1320.  
  1321.     if (*EnvString >= '0' && *EnvString <= '9')
  1322.     {
  1323.       Buffer[1] = *EnvString; // IRQ No. is 2 digits
  1324.       Buffer[2] = NULL;
  1325.       EnvString++;
  1326.     }
  1327.     else
  1328.       Buffer[1] = NULL;       // IRQ No. is 1 digit
  1329.  
  1330.     Blast->IRQNumber  = atoi(Buffer);
  1331.     IRQNotFound = FALSE;
  1332.       break;
  1333.  
  1334.  
  1335.       default:
  1336.     EnvString++;
  1337.       break;
  1338.     }
  1339.  
  1340.   } while (*EnvString != NULL);
  1341.  
  1342.   if (DMAChannelNotFound || IOPortNotFound || IRQNotFound)
  1343.     return(FAIL);
  1344.  
  1345.   /*--- Get the DSP version number.  The next read from the DSP will ---*/
  1346.   /*--- return the major version number.  The following read will    ---*/
  1347.   /*--- return the minor version number.                             ---*/
  1348.   ResetDSP(gBlastSet.BaseIOPort);
  1349.   DSPWrite(Blast->BaseIOPort, DSP_GET_VERSION);
  1350.   Major = DSPRead(Blast->BaseIOPort);  /* Read Major DSP version no. */
  1351.   Minor = DSPRead(Blast->BaseIOPort);  /* Read Minor DSP version no. */
  1352.   Blast->DSPVersion = (Major << 8) | Minor;
  1353.  
  1354.   return(SUCCESS);
  1355. }
  1356.  
  1357.  
  1358. /*************************************************************************
  1359. *
  1360. * FUNCTION: DSPRead()
  1361. *
  1362. * DESCRIPTION: Reads a value from the DSP Read Port.
  1363. *
  1364. * Entry: BaseIOPort - The Sound Blaster's base I/O address.
  1365. *
  1366. *************************************************************************/
  1367. short int DSPRead(short int BaseIOPort)
  1368. {
  1369.   /* Wait until DSP is ready before reading from the DSP. */
  1370.   while ((inp(BaseIOPort + DSP_DATA_AVAIL) & 0x80) == 0);
  1371.  
  1372.   /* Return value read from the Read Port. */
  1373.   return(inp(BaseIOPort + DSP_READ_PORT));
  1374. }
  1375.  
  1376.  
  1377. /*************************************************************************
  1378. *
  1379. * FUNCTION: DSPWrite()
  1380. *
  1381. * DESCRIPTION: Writes the value passed to this function to the DSP Write
  1382. *              Port.
  1383. *
  1384. * Entry: BaseIOAddr - The Sound Blaster's base I/O address.
  1385. *
  1386. *************************************************************************/
  1387. void DSPWrite(short int BaseIOAddr, short int WriteValue)
  1388. {
  1389.   /* Wait until DSP is ready before writing to the DSP. */
  1390.   while ((inp(BaseIOAddr + DSP_WRITE_PORT) & 0x80) != 0);
  1391.  
  1392.   outp(BaseIOAddr + DSP_WRITE_PORT, WriteValue);
  1393.   return;
  1394. }
  1395.  
  1396.  
  1397. /*************************************************************************
  1398. *
  1399. * FUNCTION: ResetDSP()
  1400. *
  1401. * DESCRIPTION: Self explanatory
  1402. *
  1403. * Entry: BaseIOAddr - The Sound Blaster's base I/O address.
  1404. *
  1405. *************************************************************************/
  1406. char ResetDSP(short int IOBasePort)
  1407. {
  1408.   outp(IOBasePort + DSP_RESET_PORT, 0x0001);  /* Write "1" to Reset Port. */
  1409.   delay(10);                                  /* Wait 10 mS.              */
  1410.   outp(IOBasePort + DSP_RESET_PORT, 0x0000);  /* Write "0" to Reset port. */
  1411.  
  1412.   /* Wait until data is available. (Wait while BIT-7 == 0.) */
  1413.   while ((inp(IOBasePort + DSP_DATA_AVAIL) & 0x80) == 0);
  1414.  
  1415.   if (inp(IOBasePort + DSP_READ_PORT) == DSP_READY)
  1416.     return(SUCCESS);
  1417.   return(FAIL);
  1418. }
  1419.  
  1420. /*************************************************************************
  1421. *
  1422. * FUNCTION: AllocateDMABuffer()
  1423. *
  1424. * DESCRIPTION : Allocate memory for the DMA buffer.  After memory is
  1425. *               allocated for the buffer, call OnSamePage() to verify
  1426. *               that the entire buffer is located on the same page.
  1427. *               If the buffer crosses a page boundary, allocate another
  1428. *               buffer. Continue this process until the DMA buffer resides
  1429. *               entirely within the same page.
  1430. *
  1431. *               For every malloc() called, save a pointer that points to
  1432. *               the block of memory allocated.  Deallocate ALL memory blocks
  1433. *               allocated that cross a page boundary.  Once a memory block
  1434. *               is allocated that does NOT cross a page boudary, this block
  1435. *               will be used for the DMA buffer--any previously allocated
  1436. *               memory blocks will be deallocated.
  1437. *
  1438. * ENTRY: **DMABuffer is the address of the pointer that will point to
  1439. *        the memory allocated.
  1440. *
  1441. * EXIT: If a buffer is succesfully allocated, *DMABuffer will point to
  1442. *       the buffer and the physical address of the buffer pointer will
  1443. *       be returned.
  1444. *
  1445. *       If a buffer is NOT successfully allocated, return FAIL.
  1446. *
  1447. *************************************************************************/
  1448. unsigned long int AllocateDMABuffer(unsigned char **DMABuffer,
  1449.                     unsigned short int *DMABufSize)
  1450. {
  1451.   unsigned char     BufferNotAllocated = TRUE,
  1452.             Done = FALSE,
  1453.            *PtrAllocated[50];
  1454.   short int         i,
  1455.             Index = 0;
  1456.   unsigned long int PhysAddress;
  1457.  
  1458.  
  1459.   /*--- BUMP *DMABufSize UP TO NEXT 8-BYTE BOUNDARY. -------------------*/
  1460.   /*--------------------------------------------------------------------*/
  1461.   if (*DMABufSize == 0 || *DMABufSize % 8)
  1462.     *DMABufSize = *DMABufSize + (8 - (*DMABufSize % 8));
  1463.  
  1464.   do
  1465.   {
  1466.     *DMABuffer = (unsigned char *) malloc(*DMABufSize);
  1467.  
  1468.     if (*DMABuffer != NULL)
  1469.     {
  1470.       /*--- Save the ptr for every malloc() performed ---*/
  1471.       PtrAllocated[Index] = *DMABuffer;
  1472.       Index++;
  1473.  
  1474.       /*--- If entire buffer is within one page, we're out of here! ---*/
  1475.       PhysAddress = OnSamePage(*DMABuffer, *DMABufSize);
  1476.       if (PhysAddress != FAIL)
  1477.       {
  1478.     BufferNotAllocated = FALSE;
  1479.     Done = TRUE;
  1480.       }
  1481.     }
  1482.     else
  1483.       Done = TRUE;  // malloc() couldn't supply requested memory
  1484.  
  1485.   } while (!Done);
  1486.  
  1487.  
  1488.   if (BufferNotAllocated)
  1489.   {
  1490.     Index++;             // Incr. Index so most recent malloc() gets free()d
  1491.     PhysAddress = FAIL;  // return FAIL
  1492.   }
  1493.  
  1494.   /*--- Deallocate all memory blocks crossing a page boundary ---*/
  1495.   for (i= 0; i < Index - 1; i++)
  1496.     free(PtrAllocated[i]);
  1497.  
  1498.   return(PhysAddress);
  1499. }
  1500.  
  1501.  
  1502. /**************************************************************************
  1503. *
  1504. * FUNCTION: OnSamePage()
  1505. *
  1506. * DESCRIPTION: Check the memory block pointed to by the parameter
  1507. *              passed to make sure the entire block of memory is on the
  1508. *              same page.  If a buffer DOES cross a page boundary,
  1509. *              return FAIL. Otherwise, return the physical address
  1510. *              of the beginning of the DMA buffer.
  1511. *
  1512. *              A page corresponds to the following addresses:
  1513. *
  1514. *              PAGE NO.   SEG:OFF ADDRESS          PHYSICAL ADDRESS
  1515. *              --------   ----------------------   ----------------
  1516. *                 0       0000:0000 to 0000:FFFF   00000 to 0FFFF
  1517. *                 1       1000:0000 to 1000:FFFF   10000 to 1FFFF
  1518. *                 .                 .                    .
  1519. *                 .                 .                    .
  1520. *                 E       E000:0000 to E000:FFFF   E0000 to EFFFF
  1521. *                 F       F000:0000 to F000:FFFF   F0000 to FFFFF
  1522. *
  1523. *              NOTE: The upper nibble of the physical address is the
  1524. *                    same as the page number!
  1525. *
  1526. * ENTRY: *DMABuffer - Points to beginning of DMA buffer.
  1527. *
  1528. * EXIT: If the buffer is located entirely within one page, return the
  1529. *       physical address of the buffer pointer.  Otherwise return FAIL.
  1530. *
  1531. **************************************************************************/
  1532. unsigned long int OnSamePage(unsigned char *DMABuffer,
  1533.                  unsigned short int DMABufSize)
  1534. {
  1535.   unsigned long int BegBuffer,
  1536.             EndBuffer,
  1537.             PhysAddress;
  1538.  
  1539.   /*----- Obtain the physical address of DMABuffer -----*/
  1540.   BegBuffer = ((unsigned long) (FP_SEG(DMABuffer)) << 4) +
  1541.            (unsigned long) FP_OFF(DMABuffer);
  1542.   EndBuffer   = BegBuffer + DMABufSize - 1;
  1543.   PhysAddress = BegBuffer;
  1544.  
  1545.   /*-- Get page numbers for start and end of DMA buffer. --*/
  1546.   BegBuffer >>= 16;
  1547.   EndBuffer >>= 16;
  1548.  
  1549.   if (BegBuffer == EndBuffer)
  1550.     return(PhysAddress);  // Entire buffer IS on same page!
  1551.   return(FAIL); // Entire buffer NOT on same page.  Thanks Intel!
  1552. }
  1553.  
  1554.  
  1555. /*************************************************************************
  1556. *
  1557. * FUNCTION: KillVolume()
  1558. *
  1559. *************************************************************************/
  1560. void KillVolume(void)
  1561. {
  1562.   // Only SB 2 with CD interface has a mixer chip.
  1563.   if (gBlastSet.DSPVersion < 0x0300)         // Select master volume reg.
  1564.     outp(gBlastSet.BaseIOPort + 4, 0x0002);
  1565.   else  // SB Pro or SB16/AWE32 mixer.
  1566.     outp(gBlastSet.BaseIOPort + 4, 0x0022);  // Select master volume reg.
  1567.  
  1568.   outp(gBlastSet.BaseIOPort + 5, 0x0000);    // KILL the volume.
  1569.  
  1570.   return;
  1571. }
  1572.  
  1573.  
  1574. /*************************************************************************
  1575. *
  1576. * FUNCTION: RestoreOrRememberVolume()
  1577. *
  1578. *************************************************************************/
  1579. void RestoreOrRememberVolume(char Command)
  1580. {
  1581.   static short int MasterVolume = 0;
  1582.  
  1583.  
  1584.   if (gBlastSet.DSPVersion < 0x0300)         // Only SB 2 with CD has a mixer.
  1585.     outp(gBlastSet.BaseIOPort + 4, 0x0002);  // Select master volume reg.
  1586.   else // SB Pro or SB16/AWE32 mixer.
  1587.     outp(gBlastSet.BaseIOPort + 4, 0x0022);  // Select master volume reg.
  1588.  
  1589.  
  1590.   if (Command == REMEMBER_VOLUME)
  1591.     MasterVolume = inp(gBlastSet.BaseIOPort + 5);  // Save master volume.
  1592.   else if (Command == RESTORE_VOLUME)
  1593.     outp(gBlastSet.BaseIOPort + 5, MasterVolume);  // Restore master volume.
  1594.  
  1595.   return;
  1596. }
  1597.  
  1598.  
  1599. /*************************************************************************
  1600. *
  1601. * FUNCTION: ContinueDMA()
  1602. *
  1603. * DESCRIPTION: Continues the DMA transfer that was halted.  The DMA
  1604. *              tranfer can be halted by calling PauseDMA().
  1605. *
  1606. * SEE ALSO: PauseDMA()
  1607. *
  1608. *************************************************************************/
  1609. void ContinueDMA(unsigned char BitsPerSample)
  1610. {
  1611.  
  1612.   if (gBlastSet.DSPVersion >= 0x0200 && gBlastSet.DSPVersion < 0x0400)
  1613.     RestoreOrRememberVolume(RESTORE_VOLUME);
  1614.  
  1615.   /*--- IF IN HIGH-SPEED MODE, CAN'T REPROGRAM DSP CHIP--RETURN! -------*/
  1616.   /*--- OTHERWISE, RESUME THE DMA.                               -------*/
  1617.   /*--------------------------------------------------------------------*/
  1618.   if (gHighSpeed == TRUE)
  1619.     return;
  1620.   else if (BitsPerSample == 8)
  1621.     DSPWrite(gBlastSet.BaseIOPort, 0x00D4);  // Continue SB 8-bit DMA xfer.
  1622.   else  // BitsPerSample == 16
  1623.     DSPWrite(gBlastSet.BaseIOPort, 0x00D6);  // Continue SB 16-bit DMA xfer.
  1624.  
  1625.   return;
  1626. }
  1627.  
  1628.  
  1629. /*************************************************************************
  1630. *
  1631. * FUNCTION: PauseDMA()
  1632. *
  1633. * DESCRIPTION: Halts the DMA tranfer.  The DMA tranfer can be resumed by
  1634. *              calling ContinueDMA().
  1635. *
  1636. * SEE ALSO: ContinueDMA()
  1637. *
  1638. *************************************************************************/
  1639. void PauseDMA(unsigned char BitsPerSample)
  1640. {
  1641.  
  1642.   if (gBlastSet.DSPVersion >= 0x0200 && gBlastSet.DSPVersion < 0x0400)
  1643.   {
  1644.     RestoreOrRememberVolume(REMEMBER_VOLUME);
  1645.     KillVolume();
  1646.   }
  1647.  
  1648.   /*--- IF IN HIGH-SPEED MODE, CAN'T REPROGRAM DSP CHIP--RETURN. -------*/
  1649.   /*--- OTHERWISE, HALT THE DMA.                                 -------*/
  1650.   /*--------------------------------------------------------------------*/
  1651.   if (gHighSpeed == TRUE)
  1652.     return;
  1653.   else if (BitsPerSample == 8)
  1654.     DSPWrite(gBlastSet.BaseIOPort, 0x00D0);  // Pause SB 8-bit DMA xfer.
  1655.   else  // BitsPerSample == 16
  1656.     DSPWrite(gBlastSet.BaseIOPort, 0x00D5);  // Pause SB 16-bit DMA xfer.
  1657.  
  1658.   return;
  1659. }
  1660.  
  1661.  
  1662. /*************************************************************************
  1663. *
  1664. * FUNCTION: SetMixer()
  1665. *
  1666. * DESCRIPTION: Sets mixer to maximum volume.
  1667. *
  1668. *************************************************************************/
  1669. void SetMixer(void)
  1670. {
  1671.   outp(gBlastSet.BaseIOPort + 4, (int) 0x0A);
  1672.   outp(gBlastSet.BaseIOPort + 5, (int) 0x00);
  1673.   outp(gBlastSet.BaseIOPort + 4, (int) 0x04);
  1674.   outp(gBlastSet.BaseIOPort + 5, (int) 0xFF);
  1675.   outp(gBlastSet.BaseIOPort + 4, (int) 0x22);
  1676.   outp(gBlastSet.BaseIOPort + 5, (int) 0xFF);
  1677.  
  1678.   return;
  1679. }
  1680.  
  1681.  
  1682. /*************************************************************************
  1683. *
  1684. * FUNCTION: AcquireCSP()
  1685. *
  1686. * DESCRIPTION: Get the CSP and download code from file into it.
  1687. *
  1688. **************************************************************************/
  1689. #ifdef USE_CSP
  1690. char AcquireCSP(CSP_FUNC_PTR CSPFunc, unsigned short int FileFormat)
  1691. {
  1692.   char          FileName[80],
  1693.            *SoundDir;
  1694.   int           FileLength,
  1695.         Handle;
  1696.   DWORD         RetValue;
  1697.   unsigned int  NoOfBytesRead,
  1698.         Segment,
  1699.         Temp;
  1700.   void far     *Buffer;
  1701.  
  1702.  
  1703.   /*--- GET FILE-PATH NAME OF FILE CONTAINING CSP CODE -----------------*/
  1704.   /*--------------------------------------------------------------------*/
  1705.   SoundDir = getenv("SOUND");
  1706.   if (SoundDir == 0)
  1707.   {
  1708.     puts("SOUND environment variable missing!");
  1709.     return(FAIL);
  1710.   }
  1711.   strcpy(FileName, SoundDir);
  1712.  
  1713.   switch(FileFormat)
  1714.   {
  1715.     case 0x0006:
  1716.       strcat(FileName, "\\CSP\\WO0006.CSP");
  1717.     break;
  1718.  
  1719.     case 0x0007:
  1720.       strcat(FileName, "\\CSP\\WO0007.CSP");
  1721.     break;
  1722.  
  1723.     case 0x0200:
  1724.       strcat(FileName, "\\CSP\\WO0200.CSP");
  1725.     break;
  1726.  
  1727.     default:
  1728.       puts("Invalid FileFormat in AcquireCSP().");
  1729.     break;
  1730.   }
  1731.  
  1732.   /*--- OPEN FILE CONTAINING CSP CODE ----------------------------------*/
  1733.   /*--------------------------------------------------------------------*/
  1734.   Handle = open(FileName, O_RDONLY);
  1735.   if (Handle == -1)
  1736.   {
  1737.  
  1738.     // Check for file in current directory.
  1739.     switch(FileFormat)
  1740.     {
  1741.       case 0x0006:
  1742.     Handle = open("WO0006.CSP", O_RDONLY);
  1743.       break;
  1744.  
  1745.       case 0x0007:
  1746.     Handle = open("WO0007.CSP", O_RDONLY);
  1747.       break;
  1748.  
  1749.       case 0x0200:
  1750.     Handle = open("WO0200.CSP", O_RDONLY);
  1751.       break;
  1752.  
  1753.       default:
  1754.     puts("Invalid FileFormat in AcquireCSP().");
  1755.       break;
  1756.     }
  1757.  
  1758.     if (Handle == -1)
  1759.     {
  1760.       printf("Can't open file: %s\n", FileName);
  1761.       return(FAIL);
  1762.     }
  1763.   }
  1764.  
  1765.   /*--- ALLOCATE MEMORY TO LOAD CSP CODE. ------------------------------*/
  1766.   /*--------------------------------------------------------------------*/
  1767.   FileLength = filelength(Handle);
  1768.   if (allocmem(FileLength / 16 + 1, &Segment) != -1)
  1769.   {
  1770.     puts("allocmem() failed!");
  1771.     close(Handle);
  1772.     return(FAIL);
  1773.   }
  1774.  
  1775.   /*--- COPY CODE FROM FILE INTO ALLOCATED MEMORY. ---------------------*/
  1776.   /*--------------------------------------------------------------------*/
  1777.   FP_SEG(Buffer) = Segment;
  1778.   FP_OFF(Buffer) = 0;
  1779.  
  1780.   if (_dos_read(Handle, Buffer, FileLength, &NoOfBytesRead) != 0)
  1781.   {
  1782.     puts("read() FAILS!");
  1783.     freemem(Segment);
  1784.     close(Handle);
  1785.     return(FAIL);
  1786.   }
  1787.  
  1788.   close(Handle);
  1789.  
  1790.   /*--- ACQUIRE CSP AND GET THE USER NUMBER ----------------------------*/
  1791.   /*--------------------------------------------------------------------*/
  1792.   RetValue = CSPFunc(0, CSPSYS_ACQUIRE, UNUSED, (DWORD) &gUserNo, UNUSED);
  1793.   if (RetValue != CSPSYS_ERR_NOERROR)
  1794.   {
  1795.     freemem(Segment);
  1796.     puts("CSPFunc() CSPSYS_ACQUIRE fails!");
  1797.     return(FAIL);
  1798.   }
  1799.  
  1800.   /*--- OPEN TEMPORARY FILE TO SAVE USER NUMBER ------------------------*/
  1801.   /*--------------------------------------------------------------------*/
  1802.   Handle = open(gCSPUserFileName, O_CREAT | O_WRONLY | O_TRUNC,
  1803.         S_IWRITE | S_IWRITE);
  1804.   if (Handle == -1)
  1805.   {
  1806.     printf("Can't create file: %s\n", gCSPUserFileName);
  1807.     goto exit_fail;
  1808.   }
  1809.  
  1810.   /*--- SAVE USER NUMBER TO TEMPORARY FILE -----------------------------*/
  1811.   /*--------------------------------------------------------------------*/
  1812.   if (write(Handle, &gUserNo, sizeof(gUserNo)) == -1)
  1813.   {
  1814.     puts("write() FAILS!");
  1815.     close(Handle);
  1816.     goto exit_fail;
  1817.   }
  1818.  
  1819.   close(Handle);  // Close the temporary file.
  1820.  
  1821.   /*--- DOWNLOAD CSP CODE FROM BUFFER TO CSP CHIP ----------------------*/
  1822.   /*--------------------------------------------------------------------*/
  1823.   RetValue = CSPFunc(0, CSPSYS_DOWNLOAD, gUserNo, (DWORD) Buffer, FileLength);
  1824.   if (RetValue != CSPSYS_ERR_NOERROR)
  1825.   {
  1826.     puts("CSPFunc() CSPSYS_DOWNLOAD fails!");
  1827.     goto exit_fail;
  1828.   }
  1829.  
  1830.   /*--- SET CSP NUMBER OF CHANNELS -------------------------------------*/
  1831.   /*--------------------------------------------------------------------*/
  1832.   RetValue = CSPFunc(0, CSPSYS_SETPARAM, gUserNo, CSPSYS_PARAM_NCHANNELS,
  1833.              gChannels);
  1834.   if (RetValue != CSPSYS_ERR_NOERROR)
  1835.   {
  1836.     puts("CSPFunc() CSPSYS_SETPARM NCHANNELS fails!");
  1837.     goto exit_fail;
  1838.   }
  1839.  
  1840.   /*--- SET CSP DMA WIDTH ----------------------------------------------*/
  1841.   /*--------------------------------------------------------------------*/
  1842.   if (FileFormat == 0x0200)
  1843.     Temp = 16;  // Force to 16 because CTADPCM files have BitsPerSample = 4.
  1844.   else
  1845.     Temp = gBitsPerSample;
  1846.  
  1847.   RetValue = CSPFunc(0, CSPSYS_SETPARAM, gUserNo, CSPSYS_PARAM_DMAWIDTH, Temp);
  1848.   if (RetValue != CSPSYS_ERR_NOERROR)
  1849.   {
  1850.     puts("CSPFunc() CSPSYS_SETPARM DMAWIDTH fails!");
  1851.     goto exit_fail;
  1852.   }
  1853.  
  1854.   /*--- START CSP RUNNING ----------------------------------------------*/
  1855.   /*--------------------------------------------------------------------*/
  1856.   RetValue = CSPFunc(0, CSPSYS_START, gUserNo, UNUSED, UNUSED);
  1857.   if (RetValue != CSPSYS_ERR_NOERROR)
  1858.   {
  1859.     puts("CSPFunc() CSPSYS_START fails!");
  1860.     goto exit_fail;
  1861.   }
  1862.  
  1863.   freemem(Segment);
  1864.   puts("CSP ACQUIRED");
  1865.   return(SUCCESS);
  1866.  
  1867.  
  1868. exit_fail:
  1869.  
  1870.   freemem(Segment);
  1871.  
  1872.   /*--- RELEASE THE CSP ------------------------------------------------*/
  1873.   /*--------------------------------------------------------------------*/
  1874.   RetValue = CSPFunc(0, CSPSYS_RELEASE, gUserNo, UNUSED, UNUSED);
  1875.   if (RetValue != CSPSYS_ERR_NOERROR)
  1876.     puts("AcquireCSP() call to CSPFunc() CSPSYS_RELEASE fails!");
  1877.  
  1878.   return(FAIL);
  1879. }
  1880. #endif
  1881.  
  1882.  
  1883. /**************************************************************************
  1884. *
  1885. * FUNCTION: ReleaseCSP()
  1886. *
  1887. * DESCRIPTION:
  1888. *
  1889. **************************************************************************/
  1890. #ifdef USE_CSP
  1891. char ReleaseCSP(CSP_FUNC_PTR CSPFunc)
  1892. {
  1893.   int   Handle;
  1894.   DWORD RetValue;
  1895.  
  1896.   /*--- OPEN TEMPORARY FILE CREATED IN AcquireCSP() ---------------------*/
  1897.   /*---------------------------------------------------------------------*/
  1898.   Handle = open(gCSPUserFileName, O_RDONLY);
  1899.   if (Handle == -1)
  1900.   {
  1901.     printf("ReleaseCSP() can't open file: %s\n", gCSPUserFileName);
  1902.     return(FAIL);
  1903.   }
  1904.  
  1905.   /*--- READ THE USER NUMBER FROM THE TEMPORARY FILE. ------------------*/
  1906.   /*--------------------------------------------------------------------*/
  1907.   if (read(Handle, &gUserNo, sizeof(gUserNo)) == -1)
  1908.   {
  1909.     printf("ReleaseCSP() can't read: %s\n", gCSPUserFileName);
  1910.     return(FAIL);
  1911.   }
  1912.  
  1913.   /*--- CLOSE AND DELETE THE TEMPORARY FILE. ---------------------------*/
  1914.   /*--------------------------------------------------------------------*/
  1915.   close(Handle);
  1916.   unlink(gCSPUserFileName);
  1917.  
  1918.  
  1919.   /*--- STOP THE CSP ---------------------------------------------------*/
  1920.   /*--------------------------------------------------------------------*/
  1921.   RetValue = CSPFunc(0, CSPSYS_STOP, gUserNo, UNUSED, UNUSED);
  1922.   if (RetValue != CSPSYS_ERR_NOERROR)
  1923.   {
  1924.     puts("ReleaseCSP() call to CSPFunc() CSPSYS_STOP fails!");
  1925.     return(FAIL);
  1926.   }
  1927.  
  1928.  
  1929.   /*--- RELEASE THE CSP ------------------------------------------------*/
  1930.   /*--------------------------------------------------------------------*/
  1931.   RetValue = CSPFunc(0, CSPSYS_RELEASE, gUserNo, UNUSED, UNUSED);
  1932.   if (RetValue != CSPSYS_ERR_NOERROR)
  1933.   {
  1934.     puts("ReleaseCSP() call to CSPFunc() CSPSYS_RELEASE fails!");
  1935.     return(FAIL);
  1936.   }
  1937.  
  1938.   puts("CSP RELEASED");
  1939.   return(SUCCESS);
  1940. }
  1941. #endif
  1942.  
  1943.  
  1944.  
  1945. /************************************************************************
  1946. *
  1947. * FUNCTION: GetCSPDriverAddr()
  1948. *
  1949. * DESCRIPTION: Points CSPFunc() function pointer to the driver's address
  1950. *              so the CSP.SYS driver can be called later.
  1951. *
  1952. *************************************************************************/
  1953. #ifdef USE_CSP
  1954. char GetCSPDriverAddr(CSP_FUNC_PTR *CSPFunc)
  1955. {
  1956.   int Handle;
  1957.  
  1958.   /*--- OPEN CSP.SYS DRIVER --------------------------------------------*/
  1959.   /*--------------------------------------------------------------------*/
  1960.   Handle = open("CSPXXXX0", O_RDONLY);
  1961.   if (Handle == -1)
  1962.   {
  1963.     puts("CSP.SYS driver did not open!");
  1964.     return (FAIL);
  1965.   }
  1966.  
  1967.   /*--- Point CSPFunc to the CSP driver's address. ---------------------*/
  1968.   /*--------------------------------------------------------------------*/
  1969.   if (ioctl(Handle, 2, CSPFunc, 4) == -1)
  1970.   {
  1971.     puts("ioctl() FAILS!");
  1972.     close(Handle);
  1973.     return(FAIL);
  1974.   }
  1975.  
  1976.   close(Handle);
  1977.  
  1978.   return (SUCCESS);
  1979. }
  1980. #endif
  1981.